<?php
/**
 *
 *
 * @auther Administrator
 * @create 2009/07/12
 */
require_once 'XML/Serializer.php';
require_once 'XML/Unserializer.php';

abstract class Lib_Abstract_Xml
{
	/**
	 * コンストラクタの処理結果正常
	 * @var string
	 */
	const RET_NOMAL = '00';

	/**
	 * コンストラクタの処理結果エラー
	 * @var string
	 */
	const RET_ERROR = '01';

	/**
	 * 格納テキストファイル名
	 * @var string
	 */
	protected $_strFileName;

	/**
	 * 配列→XMLストリングオブジェクト
	 * @var XML_Serializer
	 */
	protected $_objSerializer;

	/**
	 * XMLストリング→配列オブジェクト
	 * @var XML_Unserializer
	 */
	protected $_objUnserializer;

	/**
	 * 反復が想定されるタグの配列
	 * @var array
	 */
	protected $_aryForceEnum;

	/**
	 * XML連想配列
	 * @var array
	 */
	protected $_aryXml;

	/**
	 * 構造検索結果の該当エントリまでの構造をレベル毎に保持する
	 * @var array
	 */
	protected $_arySearchResultStructureLevel;

	/**
	 * キーに対応する値のリファレンスを返す
	 * 構造化配列に再帰検索を実施した結果
	 * @var array
	 */
	protected $_arySearchKeys;

	/**
	 * コンストラクタの処理結果コードを保持
	 * @var unknown_type
	 */
	protected $_strRet;

	/**
	 * コンストラクター
	 * @param $strFileName
	 * @return void
	 */
	public function __construct( $strFileName )
	{
		$this->_strRet = self::RET_NOMAL;

		if ( !file_exists( $strFileName ) ) {
			$this->_strFileName = $strFileName;
			$this->_strRet = self::RET_ERROR;
			return;
		}

		$strXml = file_get_contents( $strFileName );
		if ( $strXml === FALSE ) {
			$this->_strFileName = '';
			$this->_strRet = self::RET_ERROR;
			return;
		}

		$this->_strFileName = $strFileName;
		$this->_setForceEnum();
		$this->_objUnserializer =new XML_Unserializer();
		$this->_objUnserializer->setOption('forceEnum', $this->_aryForceEnum );
		$this->_objUnserializer->unserialize( $strXml );
		$this->_aryXml = $this->_objUnserializer->getUnserializedData();
		$this->deleteNullRec();
	}

	/**
	 * 配列をXML形式にシリアライズしてテキストファイルに保存する
	 * @param $strFileName
	 * @return unknown_type
	 */
	public function saveToFile( $strFileName = '' )
	{
		if ( $strFileName == '' ) {
			$strFileName = $this->_strFileName;
		}

		$options = array(
		      "indent" => "\t",
		      "linebreak" => "\n",
		      "typeHints" => false,
		      "addDecl" => true,
		      "encoding" => "UTF-8",
		      "rootName" => "DATA",
		      XML_SERIALIZER_OPTION_RETURN_RESULT => true
		);


		$objSerial = new XML_Serializer($options);
		$objSerial->setOption('mode','simplexml');
		$strXml = $objSerial->serialize($this->_aryXml);
		file_put_contents($strFileName, $strXml);
	}

	/**
	 * 全消去
	 */
	public function clear()
	{
		$this->_aryXml = array();
	}
	
	/**
	 * XML配列を返す
	 * @return array | string
	 */
	public function getArrayData()
	{
		if ( $this->_strRet != self::RET_NOMAL ) {
			return $this->_strRet;
		}

		return $this->_aryXml;
	}

	/**
	 * カレントノードに子ノードを追加する
	 * @param $aryCurrent カレントノード
	 * @param $aryChild 子ノード
	 * @see 子ノードと同一キーがカレントの子ノードに既存の場合、値が上書きされる
	 * @see このーどのキーが数字の場合、キーを更新して追加する
	 * @return void
	 */
	public function addChild( &$aryCurrent, $aryChild )
	{
		//$aryCurrent = array_merge( $aryCurrent, $aryChild );
		$aryCurrent[] = $aryChild;
	}

	/**
	 * カレントノードの子ノードを更新する
	 * @param $aryCurrent
	 * @param $aryChild
	 * @see カレントの子ノードに、存在しないキーがaryChildにアル場合、値が追加される
	 * @return unknown_type
	 */
	public function replaceChild( &$aryCurrent, $aryChild )
	{
		$aryChildKeys = array_keys( $aryChild );
		foreach ( $aryChildKeys as $strChildKey ) {
			unset ( $aryCurrent[$strChildKey] );
		}
		$this->addChild( $aryCurrent, $aryChild );
	}

	/**
	 * 構造化配列から指定キーと値が一致する配列構造を返す
	 * @param $arySrc
	 * @param $strKey
	 * @param $strValue
	 * @return array | boolean
	 * @see 見つからない場合FALSEをかえす
	 */
	protected function &_searchStructArray( &$arySrc, $strKey, $strValue )
	{
		foreach ( $arySrc as $strCheckKey=>&$vCheck ) {
			if ( is_array( $vCheck ) ) {
				$aryResult = &$this->_searchStructArray( $vCheck, $strKey, $strValue );
				if ( $aryResult !== FALSE ) {
					$this->_arySearchResultStructureLevel[] = &$vCheck;
					return $aryResult;
				}
			} else {
				if ( $strCheckKey == $strKey ) {
					if ( $vCheck == $strValue ) {
						return $arySrc;
					}
				}
			}
		}
		$bResult = FALSE;
		return $bResult;
	}

	protected function _searchKeys ( $strKey ) {
		$this->_arySearchKeys = array();
		$this->_searchKeysNext( $this->_aryXml, $strKey );
		return $this->_arySearchKeys;
	}

	protected function _searchKeysNext( &$arySrc, $strKey ) {
		foreach ( $arySrc as $strCheckKey=>&$vCheck ) {
			if ( is_array( $vCheck ) ) {
				if ( isset( $vCheck[$strKey] ) ) {
					$this->_arySearchKeys[] = &$vCheck;
				}
				$this->_searchKeysNext( $vCheck, $strKey );
			}
		}
	}

	protected function _searchKeysText( &$arySrc, $strKey ) {
		foreach ( $arySrc as $strCheckKey=>&$vCheck ) {
			if ( is_array( $vCheck ) ) {
				$this->_searchKeysNext( $vCheck, $strKey );
			} else {
				if ( $strCheckKey == $strKey ) {
					$this->_arySearchKeys[] = &$vCheck;
				}
			}
		}
	}

	/**
	 * XMLに設定する当日日付時刻を取得する
	 * @return string
	 */
	public function getDateTime()
	{
		$strResult = date('Y-m-d H:i:s');
		return $strResult;
	}

	/**
	 * 多階層配列の構造をチェックする
	 * @param $arySrc　チェックする配列
	 * @param $aryKey　下り階層キー配列
	 * @return boolean
	 * @see array("a"=>array("b"=>array("c"=>""))) にたいし
	 * @see ::isset_array($arySrc, array("a","b","c") );はTRUEを返す
	 * @see 終端が文字列の場合、終端が数字キーの配列であるかの判定はできない。
	 * @see 終端が配列であるかの確認は、isarrayを別途行うこと
	 */
	public static function isset_array( $arySrc , $aryKey )
	{
		$aryCheckBuff = $arySrc;
		foreach ( $aryKey as $strKey ) {
			if ( !isset( $aryCheckBuff[$strKey] ) ) {
				return false;
			}
			$aryCheckBuff = $aryCheckBuff[$strKey];
		}
		return true;
	}

	public function deleteNullRec()
	{
		return;
	}

	/**
	 * 反復が想定されるタグの配列を作成する
	 *
	 * @see PEAR UnserializerのforceEnum option
	 * @return unknown_type
	 */
	abstract public function _setForceEnum();


}
?>